import os.path
import time
import urllib.parse
import requests
from auto import InputInterface


class Input(InputInterface):
    def __init__(self):
        super().__init__()
        self.domain: str | None = 'localhost'
        self.port: int | None = None
        self.url: str | None = None
        self.hwnd: int | None = None
        self.pic: str = ''

    def init(self, domain, port):
        self.domain = domain or 'localhost'
        self.port = port
        # noinspection all
        self.url = f'http://{self.domain}:{self.port}'
        for i in range(10):
            if self.ver() != '':  # 检查能否获得dm版本号，能获得则说明启动成功
                break
            else:
                time.sleep(0.2)

        _ = requests.get(f"{self.url}/SetPath/{urllib.parse.quote_plus(self.root_path)}")  # 设置dm插件运行目录
        _ = self.EnableRealMouse() # 鼠标动作模拟真实操作,带移动轨迹,以及点击延时随机.
        self.initialized = True

    def EnableRealMouse(self, enable=2, mouseDelay=20, mouseStep=30):
        _ = requests.get(f"{self.url}/EnableRealMouse/{enable}/{mouseDelay}/{mouseStep}")
        return _

    def EnumWindowsWithTitleYK(self) -> dict:
        res = requests.get(f'{self.url}/EnumWindowsWithTitleYK')
        if res.status_code != 200:
            return {"suc": False, "msg": "获取窗口列表失败"}
        else:
            return res.json()

    def CaptureYk(self, x1: int = 0, y1: int = 0, x2: int = 0, y2: int = 0):
        if self.hwnd is None:
            return {"suc": False, "msg": "请先绑定窗口"}
        res = requests.get(f'{self.url}/CapturePicYk/x1/y1/x2/y2')
        if res.status_code != 200:
            return {"suc": False, "msg": "截图失败"}
        else:
            pic = res.json().get('image')
            self.pic = pic
            return {"suc": True, "image": pic}

    def capture(self, x1: int = 0, y1: int = 0, x2: int = 0, y2: int = 0, file: str = 'tmp.png'):
        """
        将截图保存到png图像，并返回一个base64字符串

        :param x1:
        :param y1:
        :param x2:
        :param y2:
        :param file:
        :return:
        """
        if self.hwnd is None:
            hwnd = self.GetBindWindow()
            if hwnd != -1:
                self.hwnd = hwnd
            else:
                return {"suc": False, "msg": "请先绑定窗口"}
        file = urllib.parse.quote_plus(str(file))
        res = requests.get(f'{self.url}/CapturePng/{x1}/{y1}/{x2}/{y2}/{file}')
        if res.status_code != 200:
            return {"suc": False, "msg": "截图失败"}
        else:
            pic = res.json().get('image')  # 带data:image/base64编码的字符串
            self.pic = pic
            self.save_base64_to_file(pic, os.path.abspath(file))
            return {"suc": True, "image": pic, "image_path": os.path.abspath(file)}

    def wait_for_exe_ready(self, title, timeout=5, flag=0) -> int:
        """
        等待一定时间，直到指定窗口出现或超时

        :param title:
        :param timeout:
        :param flag: 0-严格匹配，1-模糊匹配
        :return:
        """
        start_time = time.time()
        while True:
            res: dict = self.EnumWindowsWithTitleYK()
            hwnd = -1
            for hwnd_, title1 in res.items():
                if flag == 0:
                    if title1 == title:
                        hwnd = hwnd_
                else:
                    if title1.find(title) != -1:
                        hwnd = hwnd_
            if hwnd != -1:
                self.hwnd = int(hwnd)
                return self.hwnd

            # 检查是否超时
            if time.time() - start_time >= timeout:
                return -1

            time.sleep(1)

    def find_window_by_pid(self, pid):
        ...

    def FindWindowEx(self, parent, class_name='null', title='null'):
        """
        查找符合类名或者标题名的顶层可见窗口,如果指定了parent,则在parent的第一层子窗口中查找.

        :param parent: 父窗口句柄，如果为空，则匹配所有顶层窗口
        :param class_name: 窗口类名，如果为空，则匹配所有. 这里的匹配是模糊匹配
        :param title: 窗口标题,如果为空，则匹配所有. 这里的匹配是模糊匹配
        :return: 没有找到窗口则返回-1或0
        """
        encoded_class_name = urllib.parse.quote_plus(str(class_name))
        encoded_title = urllib.parse.quote_plus(str(title))
        # http://localhost:8765/FindWindowEx/7671946/null/play
        res = requests.get(f'{self.url}/FindWindowEx/{parent}/{encoded_class_name}/{encoded_title}')
        if res.status_code != 200:
            return -1
        else:
            return int(res.text)

    def EnumWindow(self, parent, title, class_name, flag) -> list[int]:
        """
        根据指定条件,枚举系统中符合条件的窗口,可以枚举到按键自带的无法枚举到的窗口，找不到返回空列表

        参数定义:
        parent 整形数: 获得的窗口句柄是该窗口的子窗口的窗口句柄,取0时为获得桌面句柄
        title 字符串: 窗口标题. 此参数是模糊匹配.
        class_name 字符串: 窗口类名. 此参数是模糊匹配.
        filter整形数: 取值定义如下
            1 : 匹配窗口标题,参数title有效
            2 : 匹配窗口类名,参数class_name有效.
            4 : 只匹配指定父窗口的第一层孩子窗口
            8 : 匹配父窗口为0的窗口,即顶级窗口
            16 : 匹配可见的窗口
            32 : 匹配出的窗口按照窗口打开顺序依次排列
            这些值可以相加,比如4+8+16就是类似于任务管理器中的窗口列表

        返回值:
        字符串: 返回所有匹配的窗口句柄字符串,格式"hwnd1,hwnd2,hwnd3"
        """
        encoded_title = urllib.parse.quote_plus(str(title))
        encoded_class_name = urllib.parse.quote_plus(str(class_name))

        res = requests.get(f'{self.url}/EnumWindow/{parent}/{encoded_title}/{encoded_class_name}/{flag}')
        if res.status_code != 200:
            return []
        else:
            res_ = res.text.split(',')
            res_ = [int(i) for i in res_]
            return res_

    def SetWindowState(self, hwnd, flag):
        """
        设置窗口状态
        0 : 关闭指定窗口
        1 : 激活指定窗口
        2 : 最小化指定窗口,但不激活
        3 : 最小化指定窗口,并释放内存,但同时也会激活窗口.(释放内存可以考虑用FreeProcessMemory函数)
        4 : 最大化指定窗口,同时激活窗口.
        5 : 恢复指定窗口 ,但不激活
        6 : 隐藏指定窗口
        7 : 显示指定窗口
        8 : 置顶指定窗口
        9 : 取消置顶指定窗口
        10 : 禁止指定窗口
        11 : 取消禁止指定窗口
        12 : 恢复并激活指定窗口
        13 : 强制结束窗口所在进程.
        14 : 闪烁指定的窗口
        15 : 使指定的窗口获取输入焦点

        :param hwnd: 需要激活的窗口句柄
        :param flag:
        :return:
        """
        return requests.get(f'{self.url}/SetWindowState/{hwnd}/{flag}')

    def BindWindow(self, hwnd, display, mouse, keypad, public='', mode=0) -> int:
        """
        绑定窗口，会自动处理特殊字符

        :param hwnd:
        :param display:
        :param mouse:
        :param keypad:
        :param public:
        :param mode:
        :return:
        """
        # "dx2","dx.mouse.position.lock.api|dx.mouse.position.lock.message|dx.mouse.clip.lock.api|dx.mouse.input.lock.api|dx.mouse.state.api|dx.mouse.api|dx.mouse.cursor","dx.keypad.input.lock.api|dx.keypad.state.api|dx.keypad.api","",0
        encoded_display = urllib.parse.quote_plus(str(display))
        encoded_mouse = urllib.parse.quote_plus(str(mouse))
        encoded_keypad = urllib.parse.quote_plus(str(keypad))
        encoded_public = urllib.parse.quote_plus(str(public))
        url = (f"{self.url}/BindWindow?hwnd={hwnd}&display={encoded_display}&mouse={encoded_mouse}"
               f"&keypad={encoded_keypad}&publicvar={encoded_public}&mode={mode}")
        res = requests.get(url)
        if res.status_code != 200:
            return 0
        return int(res.text)

    def UnBindWindow(self) -> int:
        res = requests.get(f'{self.url}/UnBindWindow')
        if res.status_code != 200:
            return 0
        res = int(res.text)
        return res

    def GetBindWindow(self) -> int:
        """
        获取已经绑定的窗口句柄，如果为-1，则表示没有绑定窗口
        :return:
        """
        res = requests.get(f'{self.url}/GetBindWindow')
        if res.status_code != 200:
            return -1
        res = int(res.text)
        return res

    def GetWindowTitle(self, hwnd):
        res = requests.get(f'{self.url}/GetWindowTitle/{hwnd}')
        if res.status_code != 200:
            return ""
        return res.text

    def reg(self, registrationCode, additionalCode) -> dict:
        """

        :param registrationCode:
        :param additionalCode:
        :return:
        """
        res = requests.get(f'{self.url}/Reg/{registrationCode}/{additionalCode}')
        if res.status_code != 200:
            return {"suc": False, "msg": "注册失败"}
        else:
            return {'suc': res.text.startswith('success'), 'msg': res.text}

    def find_pic(self, pic, sim=0.9, direction: int = 0, x1=0, y1=0, x2=0, y2=0, snapshot=None):
        """
        dm插件的找图算法很差，经常找不到图或找错图，建议自己编写找图的实现方法
        :param x1:
        :param y1:
        :param x2:
        :param y2:
        :param pic: 查找的小图片，图片可以是多种格式。如路径
        :param sim: 双精度浮点数:相似度,取值范围0.1-1.0
        :param direction: 0: 从左到右,从上到下 1: 从左到右,从下到上 2: 从右到左,从上到下 3: 从右到左, 从下到上
        :param snapshot: 查找的大图片，通常是屏幕截图
        :return:
        """
        from yangke.common.win.win_x64 import find_pic, find_pic1, find_pic_sift

        file = "window.png"
        if snapshot is None:
            self.capture(file=file)
        else:
            if snapshot.startswith("data:"):
                self.save_base64_to_file(snapshot, file)
            else:
                file = snapshot

        res = find_pic1(pic, file)
        return {"exists": res[0], "pos": res[1]}

    def ver(self):
        return requests.get(f'{self.url}/Ver').text  # '7.2520

    def left_click(self, x: int, y: int) -> int:
        res = self.move_to(x, y)
        if res != 1:
            return res
        res = requests.get(f'{self.url}/LeftClick')
        time.sleep(0.2)
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def middle_click(self, x: int, y: int):
        res = self.move_to(x, y)
        if res != 1:
            return res
        res = requests.get(f'{self.url}/MiddleClick')
        time.sleep(0.2)
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def right_click(self, x: int, y: int):
        res = self.move_to(x, y)
        if res != 1:
            return res
        res = requests.get(f'{self.url}/RightClick')
        time.sleep(0.2)
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def move_to(self, x: int, y: int):
        res = requests.get(f'{self.url}/MoveTo/{x}/{y}')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def left_down(self, x: int, y: int):
        res = self.move_to(x, y)
        if res != 1:
            return res
        res = requests.get(f'{self.url}/LeftDown')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def left_up(self):
        res = requests.get(f'{self.url}/LeftUp')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def right_down(self, x: int, y: int):
        res = self.move_to(x, y)
        if res != 1:
            return res
        res = requests.get(f'{self.url}/RightDown')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def right_up(self):
        res = requests.get(f'{self.url}/RightUp')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def middle_down(self, x: int, y: int):
        res = self.move_to(x, y)
        if res != 1:
            return res
        res = requests.get(f'{self.url}/MiddleDown')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def middle_up(self):
        res = requests.get(f'{self.url}/MiddleUp')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def wheel_up(self):
        res = requests.get(f'{self.url}/WheelUp')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def wheel_down(self):
        res = requests.get(f'{self.url}/WheelDown')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def press_key(self, key: str):
        encoded_key = urllib.parse.quote_plus(key)
        res = requests.get(f'{self.url}/KeyPressChar/{encoded_key}')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def key_down(self, key: str):
        # self.dm.KeyDownChar(key)
        encoded_key = urllib.parse.quote_plus(key)
        res = requests.get(f'{self.url}/KeyDownChar/{encoded_key}')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def key_up(self, key: str):
        # self.dm.KeyUpChar(key)
        encoded_key = urllib.parse.quote_plus(key)
        res = requests.get(f'{self.url}/KeyUpChar/{encoded_key}')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def press_key_combination(self, key: str):
        # 组合键处理，例如 "ctrl+a"
        # 这里我们直接转发给服务端处理
        encoded_key = urllib.parse.quote_plus(key)
        res = requests.get(f'{self.url}/KeyPressCombination/{encoded_key}')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def send_string(self, text: str):
        # 发送字符串
        encoded_text = urllib.parse.quote_plus(text)
        res = requests.get(f'{self.url}/SendString/{self.hwnd}/{encoded_text}')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def send_string_ime(self, text: str):
        # 使用IME发送字符串
        encoded_text = urllib.parse.quote_plus(text)
        res = requests.get(f'{self.url}/SendStringIme/{encoded_text}')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def send_string_ime2(self, text: str):
        # 使用IME发送字符串的另一种方式
        encoded_text = urllib.parse.quote_plus(text)
        res = requests.get(f'{self.url}/SendStringIme2/{self.hwnd}/{encoded_text}/0')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def key_press_str(self, text: str, delay_ms=100):
        # 只支持标准ASCII可见字符的字符串按键
        encoded_text = urllib.parse.quote_plus(text)
        res = requests.get(f'{self.url}/KeyPressStr/{encoded_text}/{delay_ms}')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def set_clipboard(self, text: str):
        # 设置剪切板内容
        encoded_text = urllib.parse.quote_plus(text)
        res = requests.get(f'{self.url}/SetClipboard/{encoded_text}')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)

    def send_paste(self, x: int = None, y: int = None):
        # 粘贴内容
        if x is not None and y is not None:
            self.move_to(x, y)
        res = requests.get(f'{self.url}/SendPaste/{self.hwnd}')
        if res.status_code != 200:
            return 0
        else:
            return int(res.text)
